
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="zh_Hans">
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>编写自定义模型字段(model fields) &#8212; Django 3.2.6.dev 文档</title>
    <link rel="stylesheet" href="../_static/default.css" type="text/css" />
    <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
    <script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
    <script type="text/javascript" src="../_static/jquery.js"></script>
    <script type="text/javascript" src="../_static/underscore.js"></script>
    <script type="text/javascript" src="../_static/doctools.js"></script>
    <script type="text/javascript" src="../_static/language_data.js"></script>
    <link rel="index" title="索引" href="../genindex.html" />
    <link rel="search" title="搜索" href="../search.html" />
    <link rel="next" title="自定义查询器" href="custom-lookups.html" />
    <link rel="prev" title="编写自定义 django-admin 命令" href="custom-management-commands.html" />



 
<script src="../templatebuiltins.js"></script>
<script>
(function($) {
    if (!django_template_builtins) {
       // templatebuiltins.js missing, do nothing.
       return;
    }
    $(document).ready(function() {
        // Hyperlink Django template tags and filters
        var base = "../ref/templates/builtins.html";
        if (base == "#") {
            // Special case for builtins.html itself
            base = "";
        }
        // Tags are keywords, class '.k'
        $("div.highlight\\-html\\+django span.k").each(function(i, elem) {
             var tagname = $(elem).text();
             if ($.inArray(tagname, django_template_builtins.ttags) != -1) {
                 var fragment = tagname.replace(/_/, '-');
                 $(elem).html("<a href='" + base + "#" + fragment + "'>" + tagname + "</a>");
             }
        });
        // Filters are functions, class '.nf'
        $("div.highlight\\-html\\+django span.nf").each(function(i, elem) {
             var filtername = $(elem).text();
             if ($.inArray(filtername, django_template_builtins.tfilters) != -1) {
                 var fragment = filtername.replace(/_/, '-');
                 $(elem).html("<a href='" + base + "#" + fragment + "'>" + filtername + "</a>");
             }
        });
    });
})(jQuery);</script>

  </head><body>

    <div class="document">
  <div id="custom-doc" class="yui-t6">
    <div id="hd">
      <h1><a href="../index.html">Django 3.2.6.dev 文档</a></h1>
      <div id="global-nav">
        <a title="Home page" href="../index.html">Home</a>  |
        <a title="Table of contents" href="../contents.html">Table of contents</a>  |
        <a title="Global index" href="../genindex.html">Index</a>  |
        <a title="Module index" href="../py-modindex.html">Modules</a>
      </div>
      <div class="nav">
    &laquo; <a href="custom-management-commands.html" title="编写自定义 &lt;code class=&#34;docutils literal notranslate&#34;&gt;&lt;span class=&#34;pre&#34;&gt;django-admin&lt;/span&gt;&lt;/code&gt; 命令">previous</a>
     |
    <a href="index.html" title="操作指南" accesskey="U">up</a>
   |
    <a href="custom-lookups.html" title="自定义查询器">next</a> &raquo;</div>
    </div>

    <div id="bd">
      <div id="yui-main">
        <div class="yui-b">
          <div class="yui-g" id="howto-custom-model-fields">
            
  <div class="section" id="s-writing-custom-model-fields">
<span id="writing-custom-model-fields"></span><h1>编写自定义模型字段(model fields)<a class="headerlink" href="#writing-custom-model-fields" title="永久链接至标题">¶</a></h1>
<div class="section" id="s-introduction">
<span id="introduction"></span><h2>介绍<a class="headerlink" href="#introduction" title="永久链接至标题">¶</a></h2>
<p><a class="reference internal" href="../topics/db/models.html"><span class="doc">字段参考</span></a> 文档介绍了如何使用 Django 的标准字段类—— <a class="reference internal" href="../ref/models/fields.html#django.db.models.CharField" title="django.db.models.CharField"><code class="xref py py-class docutils literal notranslate"><span class="pre">CharField</span></code></a>， <a class="reference internal" href="../ref/models/fields.html#django.db.models.DateField" title="django.db.models.DateField"><code class="xref py py-class docutils literal notranslate"><span class="pre">DateField</span></code></a>，等等。大多数情况下，这些类就是你需要的。虽然有时候，Django 的版本不能精确地匹配你的需求，或者你想使用的字段与 Django 内置的完全不同。</p>
<p>Django 内置的字段类型并未覆盖所有可能的数据库字段类型——只有常见的类型，例如 <code class="docutils literal notranslate"><span class="pre">VARCHAR</span></code> 和 <code class="docutils literal notranslate"><span class="pre">INTEGER</span></code>。对于更多模糊的列类型，例如地理多边形（geographic polygons），甚至是用户创建的类型，例如 <a class="reference external" href="https://www.postgresql.org/docs/current/sql-createtype.html">PostgreSQL custom types</a>，你可以自定义 Django 的 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 子类。</p>
<p>或者，你有一个复杂的 Python 对象，它可以以某种形式序列化，适应标准的数据库列类型。这是另一个 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 子类能帮助你配合模型使用你的对象的示例。</p>
<div class="section" id="s-our-example-object">
<span id="our-example-object"></span><h3>我们的示例对象<a class="headerlink" href="#our-example-object" title="永久链接至标题">¶</a></h3>
<p>创建自定义字段要求注意一些细节。为了简化问题，我们在本文档中全程使用同一实例：封装一个 Python 对象，代表手上 <a class="reference external" href="https://en.wikipedia.org/wiki/Contract_bridge">桥牌</a> 的细节。不要担心，你不需要知道如何玩桥牌就能学习此例子。你只需知道 52 张牌被均分给 4 个玩家，一般称他们 <em>北</em>，<em>东</em>，<em>南</em> 和 <em>西</em>。我们的类长这样:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Hand</span><span class="p">:</span>
    <span class="sd">&quot;&quot;&quot;A hand of cards (bridge style)&quot;&quot;&quot;</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">north</span><span class="p">,</span> <span class="n">east</span><span class="p">,</span> <span class="n">south</span><span class="p">,</span> <span class="n">west</span><span class="p">):</span>
        <span class="c1"># Input parameters are lists of cards (&#39;Ah&#39;, &#39;9s&#39;, etc.)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">north</span> <span class="o">=</span> <span class="n">north</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">east</span> <span class="o">=</span> <span class="n">east</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">south</span> <span class="o">=</span> <span class="n">south</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">west</span> <span class="o">=</span> <span class="n">west</span>

    <span class="c1"># ... (other possibly useful methods omitted) ...</span>
</pre></div>
</div>
<p>这是一个一般的 Python 类，其中没有特殊的 Django 内容。我们期望在我们的模块中做如下操作 (我们假设模型中的 <code class="docutils literal notranslate"><span class="pre">hand</span></code> 属性是 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> 的一个实例):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">example</span> <span class="o">=</span> <span class="n">MyModel</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">example</span><span class="o">.</span><span class="n">hand</span><span class="o">.</span><span class="n">north</span><span class="p">)</span>

<span class="n">new_hand</span> <span class="o">=</span> <span class="n">Hand</span><span class="p">(</span><span class="n">north</span><span class="p">,</span> <span class="n">east</span><span class="p">,</span> <span class="n">south</span><span class="p">,</span> <span class="n">west</span><span class="p">)</span>
<span class="n">example</span><span class="o">.</span><span class="n">hand</span> <span class="o">=</span> <span class="n">new_hand</span>
<span class="n">example</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
</pre></div>
</div>
<p>对模型中的 <code class="docutils literal notranslate"><span class="pre">hand</span></code> 属性的赋值与取值操作与其它 Python 类一直。技巧是告诉 Django 如何保存和加载对象。</p>
<p>为了在模型中使用 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> 类，我们 <strong>不</strong> 需要修改这个类。这很不错，因为这以为着你仅需为已存在的类编写模型支持，即便你不能修改源码。</p>
<div class="admonition note">
<p class="first admonition-title">注解</p>
<p class="last">你可能只想要自定义数据库列的优点，并在模型中像使用标准 Python 那样；字符串，或浮点数，等等。这种情况与 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> 例子类似，在进行过程中，我们将注意到差异。</p>
</div>
</div>
</div>
<div class="section" id="s-background-theory">
<span id="background-theory"></span><h2>背后的理论<a class="headerlink" href="#background-theory" title="永久链接至标题">¶</a></h2>
<div class="section" id="s-database-storage">
<span id="database-storage"></span><h3>数据库存储<a class="headerlink" href="#database-storage" title="永久链接至标题">¶</a></h3>
<p>让我们从模型字段开始吧。把它们分开来看，一个模型字段可以在处理数据库时，提供处理一般 Python 对象的方法（字符串，布尔值， <code class="docutils literal notranslate"><span class="pre">datetime</span></code> ，或像 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> 这样更复杂的类型），并将其转换成有用的格式。（对序列化而言这种格式是很有用的，但接下来我们会看到，如果数据库端处于控制之下，序列化会更简单）</p>
<p>模型中的字段必须能以某种方式转换为已存在的数据库列类型。不能的数据库提供不同的可用列类型集，但规则仍相同：你只需要处理这些类型。你想存在数据库中的任何数据都必须能适配这些类型中的某一个。</p>
<p>一般地讲，您要么编写一个 Django 字段来匹配特定的数据库列类型，要么需要一种方法将数据转换为字符串。</p>
<p>对于我们的 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> 示例，我们能将卡片数据转换为一个 104 个字符的字符串，通过以预定义的顺序连接所有卡片——也就是说，先连接 <em>北</em> 所拥有的卡，随后是 <em>东</em>， <em>南</em>，和 <em>西</em>。所有 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> 对象能被保存在数据库中的文本或字符列中。</p>
</div>
<div class="section" id="s-what-does-a-field-class-do">
<span id="what-does-a-field-class-do"></span><h3>一个字段(Field)类做了什么？<a class="headerlink" href="#what-does-a-field-class-do" title="永久链接至标题">¶</a></h3>
<p>所有的 Django 字段（本页提到的 <em>字段</em> 均指模型字段，而不是 <a class="reference internal" href="../ref/forms/fields.html"><span class="doc">表单字段</span></a>）都是 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field" title="django.db.models.Field"><code class="xref py py-class docutils literal notranslate"><span class="pre">django.db.models.Field</span></code></a> 的子类。对于所有字段，Django 记录的大部分信息是一样的——名字，帮助文本，是否唯一，等等。存储行为由 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 处理。稍后，我们会深入了解 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 能做什么；现在， 可以说万物源于 <code class="docutils literal notranslate"><span class="pre">Field</span></code>，并在其基础上自定义了类的关键行为。</p>
<p>了解 Django 字段类不保存在模型属性中很重要。模型属性包含普通的 Python 对象。你所以定义的字段类实际上在模型类创建时在 <code class="docutils literal notranslate"><span class="pre">Meta</span></code> 类中（这是如何实现的在这里不重要）。这是因为在仅创建和修改属性时，字段类不是必须的。相反，他们提供了属性值间转换的机制，并决定了什么被存入数据库或发送给 <a class="reference internal" href="../topics/serialization.html"><span class="doc">序列化器</span></a>。</p>
<p>在你创建自定义字段时牢记这点。你所写的 Django 的 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 子类提供了多种在 Python 实例和数据库/序列化器之间的转换机制（比如，保存值和使用值进行查询之间是不同的）。听起来有点迷糊，但别担心——通过以下的例子会清晰起来。只要记住，在你需要一个自定义字段时，只需创建两个类：</p>
<ul class="simple">
<li>第一个类是用户需要操作的 Python 对象。它们会复制给模型属性，它们会为了显示而读取属性，就想这样。这里本例中的 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> 类。</li>
<li>第二类是 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 的子类。这个类知道如何在永久存储格式和 Python 格式之间来回转换。</li>
</ul>
</div>
</div>
<div class="section" id="s-writing-a-field-subclass">
<span id="writing-a-field-subclass"></span><h2>编写一个 field 子类<a class="headerlink" href="#writing-a-field-subclass" title="永久链接至标题">¶</a></h2>
<p>计划编写第一个 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field" title="django.db.models.Field"><code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code></a> 子类时，需要先想想新字段和哪个已有的 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field" title="django.db.models.Field"><code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code></a> 最相似。你会继承 Django 字段节约你的时间吗？如果不会，你需要继承 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field" title="django.db.models.Field"><code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code></a> 类，从它继承了一切。</p>
<p>初始化新字段有点麻烦，因为要从公共参数中分离你需要的参数，并将剩下的传给父类 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field" title="django.db.models.Field"><code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code></a> 的 <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> 方法（或你的父类）。</p>
<p>在本例中，我们会调用 <code class="docutils literal notranslate"><span class="pre">HandField</span></code>。（调用你的 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field" title="django.db.models.Field"><code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code></a> 子类这个主意也很不错，所以认证为一个 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field" title="django.db.models.Field"><code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code></a> 很简单。）它并不表现的像任何已存在的字段，所以我们将直接继承自 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field" title="django.db.models.Field"><code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code></a>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>

<span class="k">class</span> <span class="nc">HandField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>

    <span class="n">description</span> <span class="o">=</span> <span class="s2">&quot;A hand of cards (bridge style)&quot;</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;max_length&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="mi">104</span>
        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
</div>
<p>我们的 <code class="docutils literal notranslate"><span class="pre">HandField</span></code> 接收大多数标准字段选项（参考下面的列表），但是我们确定参数是定长的，因为它只需要保存 52 个卡片和它们的值；总计 104 个字符。</p>
<div class="admonition note">
<p class="first admonition-title">注解</p>
<p>许多 Django 模型字段可以接受并没有什么用的可选参数。比如，你可以同时将 <a class="reference internal" href="../ref/models/fields.html#django.db.models.DateField.auto_now" title="django.db.models.DateField.auto_now"><code class="xref py py-attr docutils literal notranslate"><span class="pre">auto_now</span></code></a> 和:attr:<cite>~django.db.models.DateField.auto_now</cite> 传递给  <a class="reference internal" href="../ref/models/fields.html#django.db.models.DateField" title="django.db.models.DateField"><code class="xref py py-class docutils literal notranslate"><span class="pre">django.db.models.DateField</span></code></a> ，它将无视参数 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.editable" title="django.db.models.Field.editable"><code class="xref py py-attr docutils literal notranslate"><span class="pre">editable</span></code></a> （<a class="reference internal" href="../ref/models/fields.html#django.db.models.DateField.auto_now" title="django.db.models.DateField.auto_now"><code class="xref py py-attr docutils literal notranslate"><span class="pre">auto_now</span></code></a> 被设置了就意味着 <code class="docutils literal notranslate"><span class="pre">editable=False</span></code>）。这种情况下不会抛出错误。</p>
<p class="last">这种行为简化了字段类，因为它不需要检查那些没必要出现的可选参数。它们传递所有可选参数到父类，之后就不再使用它们了。您可以更严格地设置字段可选参数，或者对当前字段设置更放任的行为，一切都有您来决定。</p>
</div>
<p><code class="docutils literal notranslate"><span class="pre">Field.__init__()</span></code> 方法接收以下参数:</p>
<ul class="simple">
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.verbose_name" title="django.db.models.Field.verbose_name"><code class="xref py py-attr docutils literal notranslate"><span class="pre">verbose_name</span></code></a></li>
<li><code class="docutils literal notranslate"><span class="pre">name</span></code></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.primary_key" title="django.db.models.Field.primary_key"><code class="xref py py-attr docutils literal notranslate"><span class="pre">primary_key</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.CharField.max_length" title="django.db.models.CharField.max_length"><code class="xref py py-attr docutils literal notranslate"><span class="pre">max_length</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.unique" title="django.db.models.Field.unique"><code class="xref py py-attr docutils literal notranslate"><span class="pre">unique</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.blank" title="django.db.models.Field.blank"><code class="xref py py-attr docutils literal notranslate"><span class="pre">blank</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.null" title="django.db.models.Field.null"><code class="xref py py-attr docutils literal notranslate"><span class="pre">null</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.db_index" title="django.db.models.Field.db_index"><code class="xref py py-attr docutils literal notranslate"><span class="pre">db_index</span></code></a></li>
<li><code class="docutils literal notranslate"><span class="pre">rel</span></code>: 用于关联字段（像是： <a class="reference internal" href="../ref/models/fields.html#django.db.models.ForeignKey" title="django.db.models.ForeignKey"><code class="xref py py-class docutils literal notranslate"><span class="pre">ForeignKey</span></code></a>）。仅用于进阶用途。</li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.default" title="django.db.models.Field.default"><code class="xref py py-attr docutils literal notranslate"><span class="pre">default</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.editable" title="django.db.models.Field.editable"><code class="xref py py-attr docutils literal notranslate"><span class="pre">editable</span></code></a></li>
<li><code class="docutils literal notranslate"><span class="pre">serialize</span></code>: 若为 <code class="docutils literal notranslate"><span class="pre">False</span></code>，字段传给 Django 的 <a class="reference internal" href="../topics/serialization.html"><span class="doc">序列化器</span></a> 时不会被序列化。默认为 <code class="docutils literal notranslate"><span class="pre">True</span></code>。</li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.unique_for_date" title="django.db.models.Field.unique_for_date"><code class="xref py py-attr docutils literal notranslate"><span class="pre">unique_for_date</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.unique_for_month" title="django.db.models.Field.unique_for_month"><code class="xref py py-attr docutils literal notranslate"><span class="pre">unique_for_month</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.unique_for_year" title="django.db.models.Field.unique_for_year"><code class="xref py py-attr docutils literal notranslate"><span class="pre">unique_for_year</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.choices" title="django.db.models.Field.choices"><code class="xref py py-attr docutils literal notranslate"><span class="pre">choices</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.help_text" title="django.db.models.Field.help_text"><code class="xref py py-attr docutils literal notranslate"><span class="pre">help_text</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.db_column" title="django.db.models.Field.db_column"><code class="xref py py-attr docutils literal notranslate"><span class="pre">db_column</span></code></a></li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.db_tablespace" title="django.db.models.Field.db_tablespace"><code class="xref py py-attr docutils literal notranslate"><span class="pre">db_tablespace</span></code></a>: 仅为创建索引，如果后端支持 <a class="reference internal" href="../topics/db/tablespaces.html"><span class="doc">tablespaces</span></a>。一般情况下你可以忽略此选项。</li>
<li><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.auto_created" title="django.db.models.Field.auto_created"><code class="xref py py-attr docutils literal notranslate"><span class="pre">auto_created</span></code></a>:若字段是自动创建的，则为 <code class="docutils literal notranslate"><span class="pre">True</span></code>，用于 <a class="reference internal" href="../ref/models/fields.html#django.db.models.OneToOneField" title="django.db.models.OneToOneField"><code class="xref py py-class docutils literal notranslate"><span class="pre">OneToOneField</span></code></a> 的模型继承。仅用于进阶用途。</li>
</ul>
<p>上述列表中所有无解释的选项与在普通 Django 字段中的作用一样。参见 <a class="reference internal" href="../ref/models/fields.html"><span class="doc">字段文档</span></a> 获取例子和细节信息。</p>
<div class="section" id="s-field-deconstruction">
<span id="s-custom-field-deconstruct-method"></span><span id="field-deconstruction"></span><span id="custom-field-deconstruct-method"></span><h3>字段解析<a class="headerlink" href="#field-deconstruction" title="永久链接至标题">¶</a></h3>
<p>与编写 <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> 方法相对是编写 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.deconstruct" title="django.db.models.Field.deconstruct"><code class="xref py py-meth docutils literal notranslate"><span class="pre">deconstruct()</span></code></a> 方法。它在 <a class="reference internal" href="../topics/migrations.html"><span class="doc">模型迁移</span></a> 期间告诉 Django 如何获取你的新字段的一个实例，并将其转为序列化形式——特别是，传递什么参数给 <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> 来重新创建它。</p>
<p>如果你未在继承的字段之前添加任何选项，就不需要编写新的 <code class="docutils literal notranslate"><span class="pre">deconstruct()</span></code> 方法。然而，如果你正在修改传递给 <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> 的参数（像 <code class="docutils literal notranslate"><span class="pre">HandField</span></code> 中的一样），你需要增补被传递的值。</p>
<p><code class="docutils literal notranslate"><span class="pre">deconstruct()</span></code> 返回包含 4 个项目的元组：字段的属性名，字段类的完整导入路径，位置参数（以列表的形式），和关键字参数（以字典的形式）。注意，这与 <a class="reference internal" href="../topics/migrations.html#custom-deconstruct-method"><span class="std std-ref">为自定义类</span></a> 的 <code class="docutils literal notranslate"><span class="pre">deconstruct()</span></code> 方法不同，它返回包含 3个项目的元组。</p>
<p>作为自定义字段的作者，你不需要担心前两个值；基类 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 已包含处理字段属性名和导入路径的代码。然后，你仍必须关注位置参数和关键字参数，这些是你最有可能改的东西。</p>
<p>例如，在 <code class="docutils literal notranslate"><span class="pre">HandField</span></code> 类中，我们总是强制设置 <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> 的长度。基类 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 中的 <code class="docutils literal notranslate"><span class="pre">deconstruct()</span></code> 方法会看到这个值，并尝试在关键字参数中返回它；因此，我们能为了可读性从关键字参数中剔除它:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>

<span class="k">class</span> <span class="nc">HandField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;max_length&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="mi">104</span>
        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">deconstruct</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">name</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">kwargs</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">deconstruct</span><span class="p">()</span>
        <span class="k">del</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">&quot;max_length&quot;</span><span class="p">]</span>
        <span class="k">return</span> <span class="n">name</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">kwargs</span>
</pre></div>
</div>
<p>若你添加了一个新的关键字参数，你需要在 <code class="docutils literal notranslate"><span class="pre">deconstruct</span></code> 中新增代码，将其值传入 <code class="docutils literal notranslate"><span class="pre">kwargs</span></code>。如果不需要字段的重构状态，比如使用默认值的情况，还应该忽略 <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> 中的值。</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>

<span class="k">class</span> <span class="nc">CommaSepField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>
    <span class="s2">&quot;Implements comma-separated storage of lists&quot;</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">separator</span><span class="o">=</span><span class="s2">&quot;,&quot;</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">separator</span> <span class="o">=</span> <span class="n">separator</span>
        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">deconstruct</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">name</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">kwargs</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">deconstruct</span><span class="p">()</span>
        <span class="c1"># Only include kwarg if it&#39;s not the default</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">separator</span> <span class="o">!=</span> <span class="s2">&quot;,&quot;</span><span class="p">:</span>
            <span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;separator&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">separator</span>
        <span class="k">return</span> <span class="n">name</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">kwargs</span>
</pre></div>
</div>
<p>更多的复杂例子超出本文的范围，但是请牢记——对于你的字段实例的任意配置，<code class="docutils literal notranslate"><span class="pre">deconstruct()</span></code> 必须返回能传递给 <code class="docutils literal notranslate"><span class="pre">__init__</span></code> 的参数重构状态。</p>
<p>如果你在父类 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 中设置了新的默认值需要额外注意；说明你希望总是包含它们，而不是在它们采用旧有值时消失。</p>
<p>另外，应当避免用位置参数来返回值，尽可能用关键字参数来返回值以保证在未来能拥有最大的兼容性。如果您在构造方法参数列表中修改参数名称比修改其位置更频繁，您可能会更喜欢位置参数，但请记住，在相当长的一段时间内（也许几年），人们将从序列化版本中重新构建您的字段，这取决于您迁移持续的时间。</p>
<p>您可以在包含字段的迁移文件里查看解析的结果，此外您可以在单元测试中通过解构和重构字段来测试。</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">name</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">kwargs</span> <span class="o">=</span> <span class="n">my_field_instance</span><span class="o">.</span><span class="n">deconstruct</span><span class="p">()</span>
<span class="n">new_instance</span> <span class="o">=</span> <span class="n">MyField</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">my_field_instance</span><span class="o">.</span><span class="n">some_attribute</span><span class="p">,</span> <span class="n">new_instance</span><span class="o">.</span><span class="n">some_attribute</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="s-changing-a-custom-field-s-base-class">
<span id="changing-a-custom-field-s-base-class"></span><h3>修改自定义字段的基类<a class="headerlink" href="#changing-a-custom-field-s-base-class" title="永久链接至标题">¶</a></h3>
<p>你可能修改自定义字段的基类，因为 Django 无法检测到修改，并为其实施迁移。例如，如果你先这样:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CustomCharField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">):</span>
    <span class="o">...</span>
</pre></div>
</div>
<p>随后决定继承自 <code class="docutils literal notranslate"><span class="pre">TextField</span></code>，你不能像这样修改子类:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CustomCharField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">TextField</span><span class="p">):</span>
    <span class="o">...</span>
</pre></div>
</div>
<p>替代方法是，你必须新建一个自定义字段类，并将你的模型指向此类:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CustomCharField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">):</span>
    <span class="o">...</span>

<span class="k">class</span> <span class="nc">CustomTextField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">TextField</span><span class="p">):</span>
    <span class="o">...</span>
</pre></div>
</div>
<p>就像文档 <a class="reference internal" href="../topics/migrations.html#migrations-removing-model-fields"><span class="std std-ref">移除字段</span></a> 中讨论的一样，你必须保留原 <code class="docutils literal notranslate"><span class="pre">CustomCharField</span></code> 类只要你还有迁移指向它。</p>
</div>
<div class="section" id="s-documenting-your-custom-field">
<span id="documenting-your-custom-field"></span><h3>为自定义字段编写文档<a class="headerlink" href="#documenting-your-custom-field" title="永久链接至标题">¶</a></h3>
<p>像之前一样，你需要为自定义字段类型编写文档，这样用户就会知道这他喵到底是啥。除了为其提供对开发者很有用的 docstring 外，你也需要让后台管理员应用程序的用户通过应用程序 <span class="xref std std-doc">django.contrib.admindocs 1</span> 看到一个关于字段类型的简单介绍。只需在自定义字段的 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.description" title="django.db.models.Field.description"><code class="xref py py-attr docutils literal notranslate"><span class="pre">description</span></code></a> 属性提供描述性文本。在上述例子中，由 <code class="docutils literal notranslate"><span class="pre">admindocs</span></code> 应用为 <code class="docutils literal notranslate"><span class="pre">HandField</span></code> 字段提供的描述是 'A hand of cards (bridge style)'。</p>
<p>在 <a class="reference internal" href="../ref/contrib/admin/admindocs.html#module-django.contrib.admindocs" title="django.contrib.admindocs: Django's admin documentation generator."><code class="xref py py-mod docutils literal notranslate"><span class="pre">django.contrib.admindocs</span></code></a> 展示的内容中，字段描述在 <code class="docutils literal notranslate"><span class="pre">field.__dict__</span></code> 中差值，它允许描述包含字段参数。例如， <a class="reference internal" href="../ref/models/fields.html#django.db.models.CharField" title="django.db.models.CharField"><code class="xref py py-class docutils literal notranslate"><span class="pre">CharField</span></code></a> 的说明是:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">description</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s2">&quot;String (up to </span><span class="si">%(max_length)s</span><span class="s2">)&quot;</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="s-useful-methods">
<span id="useful-methods"></span><h3>实用方法<a class="headerlink" href="#useful-methods" title="永久链接至标题">¶</a></h3>
<p>一旦你已创建了 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field" title="django.db.models.Field"><code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code></a> 的子类，你可能会考虑重写一些标准方法，这取决于你的字段行为。以下列表中的方法大致按重要性降序排列，即从上至下。</p>
<div class="section" id="s-custom-database-types">
<span id="s-id1"></span><span id="custom-database-types"></span><span id="id1"></span><h4>自定义数据库类型<a class="headerlink" href="#custom-database-types" title="永久链接至标题">¶</a></h4>
<p>假设你已创建了一个 PostgreSQL 自定义字段，名叫 <code class="docutils literal notranslate"><span class="pre">mytype</span></code>。你可以继承 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 并实现 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.db_type" title="django.db.models.Field.db_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">db_type()</span></code></a> 方法，像这样:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>

<span class="k">class</span> <span class="nc">MytypeField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">db_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">connection</span><span class="p">):</span>
        <span class="k">return</span> <span class="s1">&#39;mytype&#39;</span>
</pre></div>
</div>
<p>只要已建立 <code class="docutils literal notranslate"><span class="pre">MytypeField</span></code>，你就能像使用其它 <code class="docutils literal notranslate"><span class="pre">Field</span></code> 类型一样在模型中使用它:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Person</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">80</span><span class="p">)</span>
    <span class="n">something_else</span> <span class="o">=</span> <span class="n">MytypeField</span><span class="p">()</span>
</pre></div>
</div>
<p>如果您想创建一个数据库来源不明确的应用，您应该解释各个数据库列类型之间的不同之处。比如，date/time 列类型在 PostgreSQL 中被称为 <code class="docutils literal notranslate"><span class="pre">timestamp</span></code>，而在 MySQL 中则叫 <code class="docutils literal notranslate"><span class="pre">datetime</span></code>。您可以通过 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.db_type" title="django.db.models.Field.db_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">db_type()</span></code></a> 方法来检查 <code class="docutils literal notranslate"><span class="pre">connection.settings_dict['ENGINE']</span></code> 属性来进行操作。</p>
<p>例子:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">MyDateField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">db_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">connection</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">connection</span><span class="o">.</span><span class="n">settings_dict</span><span class="p">[</span><span class="s1">&#39;ENGINE&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;django.db.backends.mysql&#39;</span><span class="p">:</span>
            <span class="k">return</span> <span class="s1">&#39;datetime&#39;</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="s1">&#39;timestamp&#39;</span>
</pre></div>
</div>
<p><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.db_type" title="django.db.models.Field.db_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">db_type()</span></code></a> 和 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.rel_db_type" title="django.db.models.Field.rel_db_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">rel_db_type()</span></code></a> 方法由 Django 框架在为应用构建 <code class="docutils literal notranslate"><span class="pre">CREATE</span> <span class="pre">TABLE</span></code> 语句时调用——即你第一次创建数据表的时候。这些方法也在构建一个包含此模型字段的 <code class="docutils literal notranslate"><span class="pre">WHERE</span></code> 字句时调用——即你在利用 QuerySet 方法(<code class="docutils literal notranslate"><span class="pre">get()</span></code>, <code class="docutils literal notranslate"><span class="pre">filter()</span></code>, 和 <code class="docutils literal notranslate"><span class="pre">exclude()</span></code>)检出数据时或将此模型字段作为参数时。它们在其它时间不会被调用，故它们能承担执行有点小复杂的代码，例如上述的 <code class="docutils literal notranslate"><span class="pre">connection.settings_dict</span></code> 例子。</p>
<p>某些数据库列类型接受参数，例如 <code class="docutils literal notranslate"><span class="pre">CHAR(25)</span></code>，参数 <code class="docutils literal notranslate"><span class="pre">25</span></code> 表示列的最大长度。类似用例中，该参数若在模型中指定比硬编码在 <code class="docutils literal notranslate"><span class="pre">db_type()</span></code> 方法中更灵活。举个例子，构建 <code class="docutils literal notranslate"><span class="pre">CharMaxlength25Field</span></code> 没多大意义，如下所示:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This is a silly example of hard-coded parameters.</span>
<span class="k">class</span> <span class="nc">CharMaxlength25Field</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">db_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">connection</span><span class="p">):</span>
        <span class="k">return</span> <span class="s1">&#39;char(25)&#39;</span>

<span class="c1"># In the model:</span>
<span class="k">class</span> <span class="nc">MyModel</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="c1"># ...</span>
    <span class="n">my_field</span> <span class="o">=</span> <span class="n">CharMaxlength25Field</span><span class="p">()</span>
</pre></div>
</div>
<p>更好的方式是在运行时指定参数值——即类实例化的时候。需要像这样实现 <code class="docutils literal notranslate"><span class="pre">Field.__init__()</span></code> 即可:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># This is a much more flexible example.</span>
<span class="k">class</span> <span class="nc">BetterCharField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>
    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">max_length</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">max_length</span> <span class="o">=</span> <span class="n">max_length</span>
        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">db_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">connection</span><span class="p">):</span>
        <span class="k">return</span> <span class="s1">&#39;char(</span><span class="si">%s</span><span class="s1">)&#39;</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="n">max_length</span>

<span class="c1"># In the model:</span>
<span class="k">class</span> <span class="nc">MyModel</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="c1"># ...</span>
    <span class="n">my_field</span> <span class="o">=</span> <span class="n">BetterCharField</span><span class="p">(</span><span class="mi">25</span><span class="p">)</span>
</pre></div>
</div>
<p>最后，如果你的列真的要求配置复杂的 SQL，从 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.db_type" title="django.db.models.Field.db_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">db_type()</span></code></a> 返回 <code class="docutils literal notranslate"><span class="pre">None</span></code>。这会让 Django 创建 SQL 的代码跳过该字段。随后你需要负责为该字段在正确的表中以某种方式创建列，这种方式允许你告诉 Django 不处理此事。</p>
<p><a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.rel_db_type" title="django.db.models.Field.rel_db_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">rel_db_type()</span></code></a> 方法由字段调用，例如 <code class="docutils literal notranslate"><span class="pre">ForeignKey</span></code> 和 <code class="docutils literal notranslate"><span class="pre">OneToOneField</span></code> ，这些通过指向另一个字段来决定数据库列类型的字段。举个例子，如果你有个 <code class="docutils literal notranslate"><span class="pre">UnsignedAutoField</span></code>，你也需要指向该字段的外键使用相同的数据类型:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># MySQL unsigned integer (range 0 to 4294967295).</span>
<span class="k">class</span> <span class="nc">UnsignedAutoField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">AutoField</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">db_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">connection</span><span class="p">):</span>
        <span class="k">return</span> <span class="s1">&#39;integer UNSIGNED AUTO_INCREMENT&#39;</span>

    <span class="k">def</span> <span class="nf">rel_db_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">connection</span><span class="p">):</span>
        <span class="k">return</span> <span class="s1">&#39;integer UNSIGNED&#39;</span>
</pre></div>
</div>
</div>
<div class="section" id="s-converting-values-to-python-objects">
<span id="s-id2"></span><span id="converting-values-to-python-objects"></span><span id="id2"></span><h4>将值转为 Python 对象<a class="headerlink" href="#converting-values-to-python-objects" title="永久链接至标题">¶</a></h4>
<p>若自定义 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field" title="django.db.models.Field"><code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code></a> 处理的数据结构比字符串，日期，整型，或浮点型更复杂，你可能需要重写 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.from_db_value" title="django.db.models.Field.from_db_value"><code class="xref py py-meth docutils literal notranslate"><span class="pre">from_db_value()</span></code></a> 和 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.to_python" title="django.db.models.Field.to_python"><code class="xref py py-meth docutils literal notranslate"><span class="pre">to_python()</span></code></a>。</p>
<p>若要展示字段的子类， <code class="docutils literal notranslate"><span class="pre">from_db_value()</span></code> 将会在从数据库中载入的生命周期中调用，包括聚集和 <a class="reference internal" href="../ref/models/querysets.html#django.db.models.query.QuerySet.values" title="django.db.models.query.QuerySet.values"><code class="xref py py-meth docutils literal notranslate"><span class="pre">values()</span></code></a> 调用。</p>
<p><code class="docutils literal notranslate"><span class="pre">to_python()</span></code> 在反序列化时和为表单应用 <a class="reference internal" href="../ref/models/instances.html#django.db.models.Model.clean" title="django.db.models.Model.clean"><code class="xref py py-meth docutils literal notranslate"><span class="pre">clean()</span></code></a> 时调用。</p>
<p>作为通用规则， <code class="docutils literal notranslate"><span class="pre">to_python</span></code> 应该平滑地处理以下参数：</p>
<ul class="simple">
<li>一个正确的类型（本业持续介绍的例子 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> ）。</li>
<li>一个字符串</li>
<li><code class="docutils literal notranslate"><span class="pre">None</span></code> （若字段允许 <code class="docutils literal notranslate"><span class="pre">null=True</span></code>）</li>
</ul>
<p>在 <code class="docutils literal notranslate"><span class="pre">HandField</span></code> 类中，我们在数据库中以 VARCHAR 字段的形式存储数据，所以我们要能在 <code class="docutils literal notranslate"><span class="pre">from_db_value()</span></code> 中处理字符串和 <code class="docutils literal notranslate"><span class="pre">None</span></code>。在 <code class="docutils literal notranslate"><span class="pre">to_python()</span></code> 中，我们也需要处理 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> 实例:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">re</span>

<span class="kn">from</span> <span class="nn">django.core.exceptions</span> <span class="kn">import</span> <span class="n">ValidationError</span>
<span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
<span class="kn">from</span> <span class="nn">django.utils.translation</span> <span class="kn">import</span> <span class="n">gettext_lazy</span> <span class="k">as</span> <span class="n">_</span>

<span class="k">def</span> <span class="nf">parse_hand</span><span class="p">(</span><span class="n">hand_string</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Takes a string of cards and splits into a full hand.&quot;&quot;&quot;</span>
    <span class="n">p1</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="s1">&#39;.</span><span class="si">{26}</span><span class="s1">&#39;</span><span class="p">)</span>
    <span class="n">p2</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="s1">&#39;..&#39;</span><span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="n">p2</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">p1</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="n">hand_string</span><span class="p">)]</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">4</span><span class="p">:</span>
        <span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="n">_</span><span class="p">(</span><span class="s2">&quot;Invalid input for a Hand instance&quot;</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">Hand</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">HandField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>
    <span class="c1"># ...</span>

    <span class="k">def</span> <span class="nf">from_db_value</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">expression</span><span class="p">,</span> <span class="n">connection</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">value</span>
        <span class="k">return</span> <span class="n">parse_hand</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">to_python</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">Hand</span><span class="p">):</span>
            <span class="k">return</span> <span class="n">value</span>

        <span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">value</span>

        <span class="k">return</span> <span class="n">parse_hand</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</pre></div>
</div>
<p>注意，我们总是为这些方法返回一个 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> 实例。这就是我们要保存在模型属性中的 Python 对象类型。</p>
<p>对于 <code class="docutils literal notranslate"><span class="pre">to_python()</span></code> 来说，如果在值转换过程中出现任何问题，你应该抛出一个 <a class="reference internal" href="../ref/exceptions.html#django.core.exceptions.ValidationError" title="django.core.exceptions.ValidationError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ValidationError</span></code></a> 异常。</p>
</div>
<div class="section" id="s-converting-python-objects-to-query-values">
<span id="s-id3"></span><span id="converting-python-objects-to-query-values"></span><span id="id3"></span><h4>将 Python 转为查询值<a class="headerlink" href="#converting-python-objects-to-query-values" title="永久链接至标题">¶</a></h4>
<p>使用数据库需要双向转换，如果你重写了 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.from_db_value" title="django.db.models.Field.from_db_value"><code class="xref py py-meth docutils literal notranslate"><span class="pre">from_db_value()</span></code></a> 方法，你也必须重写 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.get_prep_value" title="django.db.models.Field.get_prep_value"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_prep_value()</span></code></a> 将 Python 对象转回查询值。</p>
<p>例子:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">HandField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>
    <span class="c1"># ...</span>

    <span class="k">def</span> <span class="nf">get_prep_value</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="k">return</span> <span class="s1">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="s1">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">l</span><span class="p">)</span> <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="p">(</span><span class="n">value</span><span class="o">.</span><span class="n">north</span><span class="p">,</span>
                <span class="n">value</span><span class="o">.</span><span class="n">east</span><span class="p">,</span> <span class="n">value</span><span class="o">.</span><span class="n">south</span><span class="p">,</span> <span class="n">value</span><span class="o">.</span><span class="n">west</span><span class="p">)])</span>
</pre></div>
</div>
<div class="admonition warning">
<p class="first admonition-title">警告</p>
<p class="last">如果你使用了 MySQL 的 <code class="docutils literal notranslate"><span class="pre">CHAR</span></code>，<code class="docutils literal notranslate"><span class="pre">VARCHAR</span></code> 或 <code class="docutils literal notranslate"><span class="pre">TEXT</span></code> 类型，你必须确保 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.get_prep_value" title="django.db.models.Field.get_prep_value"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_prep_value()</span></code></a> 总是返回一个字符串。在 MySQL 中对这些类型操作时非常灵活，甚至有时超出预期，在传入值为正数时，检出结果可能包含非期望的结果。这个问题不会在你总为 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.get_prep_value" title="django.db.models.Field.get_prep_value"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_prep_value()</span></code></a> 返回字符串类型的时候出现。</p>
</div>
</div>
<div class="section" id="s-converting-query-values-to-database-values">
<span id="s-id4"></span><span id="converting-query-values-to-database-values"></span><span id="id4"></span><h4>将查询值转为数据库值<a class="headerlink" href="#converting-query-values-to-database-values" title="永久链接至标题">¶</a></h4>
<p>某些数据类型（比如 dates）在数据库后端处理前要转为某种特定格式。 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.get_db_prep_value" title="django.db.models.Field.get_db_prep_value"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_db_prep_value()</span></code></a> 实现了这种转换。查询所以使用的连接由 <code class="docutils literal notranslate"><span class="pre">connection</span></code> 参数指定。这允许你在需要时指定后台要求的转换逻辑。</p>
<p>例如，Django 为其 <a class="reference internal" href="../ref/models/fields.html#django.db.models.BinaryField" title="django.db.models.BinaryField"><code class="xref py py-class docutils literal notranslate"><span class="pre">BinaryField</span></code></a> 利用以下方法:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">get_db_prep_value</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">connection</span><span class="p">,</span> <span class="n">prepared</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
    <span class="n">value</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_db_prep_value</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">connection</span><span class="p">,</span> <span class="n">prepared</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">connection</span><span class="o">.</span><span class="n">Database</span><span class="o">.</span><span class="n">Binary</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">value</span>
</pre></div>
</div>
<p>万一自定义字段需要与普通查询参数使用的转换不同的转换规则，你可以重写 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.get_db_prep_save" title="django.db.models.Field.get_db_prep_save"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_db_prep_save()</span></code></a>。</p>
</div>
<div class="section" id="s-preprocessing-values-before-saving">
<span id="s-id5"></span><span id="preprocessing-values-before-saving"></span><span id="id5"></span><h4>在保存前预处理数值<a class="headerlink" href="#preprocessing-values-before-saving" title="永久链接至标题">¶</a></h4>
<p>如果你要在保存前预处理值，你可以调用 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.pre_save" title="django.db.models.Field.pre_save"><code class="xref py py-meth docutils literal notranslate"><span class="pre">pre_save()</span></code></a>。举个例子，Django 的 <a class="reference internal" href="../ref/models/fields.html#django.db.models.DateTimeField" title="django.db.models.DateTimeField"><code class="xref py py-class docutils literal notranslate"><span class="pre">DateTimeField</span></code></a> 在 <a class="reference internal" href="../ref/models/fields.html#django.db.models.DateField.auto_now" title="django.db.models.DateField.auto_now"><code class="xref py py-attr docutils literal notranslate"><span class="pre">auto_now</span></code></a> 或 <a class="reference internal" href="../ref/models/fields.html#django.db.models.DateField.auto_now_add" title="django.db.models.DateField.auto_now_add"><code class="xref py py-attr docutils literal notranslate"><span class="pre">auto_now_add</span></code></a> 中利用此方法正确设置属性。</p>
<p>如果你重写了此方法，你必须在最后返回该属性的值。如果修改了值，那么你也需要更新模型属性，这样持有该引用的模型总会看到正确的值。</p>
</div>
<div class="section" id="s-specifying-the-form-field-for-a-model-field">
<span id="s-specifying-form-field-for-model-field"></span><span id="specifying-the-form-field-for-a-model-field"></span><span id="specifying-form-field-for-model-field"></span><h4>为模型字段指定表单字段<a class="headerlink" href="#specifying-the-form-field-for-a-model-field" title="永久链接至标题">¶</a></h4>
<p>为了自定义 <a class="reference internal" href="../topics/forms/modelforms.html#django.forms.ModelForm" title="django.forms.ModelForm"><code class="xref py py-class docutils literal notranslate"><span class="pre">ModelForm</span></code></a> 使用的表单属性，你必须重写 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.formfield" title="django.db.models.Field.formfield"><code class="xref py py-meth docutils literal notranslate"><span class="pre">formfield()</span></code></a>。</p>
<p>表单字段类能通过 <code class="docutils literal notranslate"><span class="pre">form_class</span></code> 和 <code class="docutils literal notranslate"><span class="pre">choices_form_class</span></code> 参数指定；如果字段指定了选项，则使用后者，反之前者。若未提供这些参数，将会使用 <a class="reference internal" href="../ref/forms/fields.html#django.forms.CharField" title="django.forms.CharField"><code class="xref py py-class docutils literal notranslate"><span class="pre">CharField</span></code></a> 或 <a class="reference internal" href="../ref/forms/fields.html#django.forms.TypedChoiceField" title="django.forms.TypedChoiceField"><code class="xref py py-class docutils literal notranslate"><span class="pre">TypedChoiceField</span></code></a>。</p>
<p>完整的 <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> 被直接传递给表单字段的 <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> 方法。一般的，你要做的全部工作就是为 <code class="docutils literal notranslate"><span class="pre">form_class</span></code> 参数配置一个合适的默认值，并在随后委托父类处理。这可能要求你编写一个自定义表单字段（甚至表单视图）。查看 <a class="reference internal" href="../topics/forms/index.html"><span class="doc">表单文件材料</span></a> 获取相关信息。</p>
<p>承接上面的例子，我们能这样编写 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.formfield" title="django.db.models.Field.formfield"><code class="xref py py-meth docutils literal notranslate"><span class="pre">formfield()</span></code></a> 方法:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">HandField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>
    <span class="c1"># ...</span>

    <span class="k">def</span> <span class="nf">formfield</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="c1"># This is a fairly standard way to set up some defaults</span>
        <span class="c1"># while letting the caller override them.</span>
        <span class="n">defaults</span> <span class="o">=</span> <span class="p">{</span><span class="s1">&#39;form_class&#39;</span><span class="p">:</span> <span class="n">MyFormField</span><span class="p">}</span>
        <span class="n">defaults</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">formfield</span><span class="p">(</span><span class="o">**</span><span class="n">defaults</span><span class="p">)</span>
</pre></div>
</div>
<p>这假定我们已导入 <code class="docutils literal notranslate"><span class="pre">MyFormField</span></code> 字段类（它有默认视图）。本页文档未覆盖编写自定义表单字段的细节。</p>
</div>
<div class="section" id="s-emulating-built-in-field-types">
<span id="s-id6"></span><span id="emulating-built-in-field-types"></span><span id="id6"></span><h4>仿造内置字段类型<a class="headerlink" href="#emulating-built-in-field-types" title="永久链接至标题">¶</a></h4>
<p>若你已创建了 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.db_type" title="django.db.models.Field.db_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">db_type()</span></code></a> 方法，你无需担心 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.get_internal_type" title="django.db.models.Field.get_internal_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_internal_type()</span></code></a> 方法——它并不常用。虽然很多时候，数据库存储行为和其他字段类似，所以你能直接用其它字段的逻辑创建正确的列。</p>
<p>例子:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">HandField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>
    <span class="c1"># ...</span>

    <span class="k">def</span> <span class="nf">get_internal_type</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s1">&#39;CharField&#39;</span>
</pre></div>
</div>
<p>无论我们使用了哪个数据库后端， <a class="reference internal" href="../ref/django-admin.html#django-admin-migrate"><code class="xref std std-djadmin docutils literal notranslate"><span class="pre">migrate</span></code></a> 或其它 SQL 命令总会在保存字符串时为其创建正确的列类型。</p>
<p>若 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.get_internal_type" title="django.db.models.Field.get_internal_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_internal_type()</span></code></a> 返回了当前数据库后端（即 <code class="docutils literal notranslate"><span class="pre">django.db.backends.&lt;db_name&gt;.base.DatabaseWrapper.data_types</span></code> 中未出现的后端）无法理解的字符串——该字符串仍会被序列化器使用的，但是默认的 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.db_type" title="django.db.models.Field.db_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">db_type()</span></code></a> 方法会返回 <code class="docutils literal notranslate"><span class="pre">None</span></code>。查阅文档 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.db_type" title="django.db.models.Field.db_type"><code class="xref py py-meth docutils literal notranslate"><span class="pre">db_type()</span></code></a> 了解为啥有用。如果您打算在 Django 之外的其他地方使用序列化器输出，那么将描述性字符串作为序列化器的字段类型是一个有用的想法。</p>
</div>
<div class="section" id="s-converting-field-data-for-serialization">
<span id="s-converting-model-field-to-serialization"></span><span id="converting-field-data-for-serialization"></span><span id="converting-model-field-to-serialization"></span><h4>为序列化转换字段数据<a class="headerlink" href="#converting-field-data-for-serialization" title="永久链接至标题">¶</a></h4>
<p>自定义序列化器序列化值的流程，你要重写 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.value_to_string" title="django.db.models.Field.value_to_string"><code class="xref py py-meth docutils literal notranslate"><span class="pre">value_to_string()</span></code></a>。使用 <a class="reference internal" href="../ref/models/fields.html#django.db.models.Field.value_to_string" title="django.db.models.Field.value_to_string"><code class="xref py py-meth docutils literal notranslate"><span class="pre">value_to_string()</span></code></a> 是在序列化之前获取字段值的最佳方法。举个例子，由于 <code class="docutils literal notranslate"><span class="pre">HandField</span></code> 使用字符串存储数据，我们能复用一些已有代码:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">HandField</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Field</span><span class="p">):</span>
    <span class="c1"># ...</span>

    <span class="k">def</span> <span class="nf">value_to_string</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
        <span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">value_from_object</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_prep_value</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
<div class="section" id="s-some-general-advice">
<span id="some-general-advice"></span><h3>一些通用建议<a class="headerlink" href="#some-general-advice" title="永久链接至标题">¶</a></h3>
<p>编写自定义字段是个棘手的，尤其是在 Python 类，数据库，序列化格式之间进行复杂转换的时候。下面有几个让事情更顺利的建议：</p>
<ol class="arabic simple">
<li>借鉴已有的 Django 字段（位于 <code class="file docutils literal notranslate"><span class="pre">django/db/models/fields/__init__.py</span></code>）。试着找到一个与你目标类似的字段，而不是从零开始创建。</li>
<li>为字段类添加一个 <code class="docutils literal notranslate"><span class="pre">__str__()</span></code> 方法。在很多地方，字段代码的默认行为是对值调用 <code class="docutils literal notranslate"><span class="pre">str()</span></code>。（本页文档中， <code class="docutils literal notranslate"><span class="pre">value</span></code> 会是一个 <code class="docutils literal notranslate"><span class="pre">Hand</span></code> 实例，而不是 <code class="docutils literal notranslate"><span class="pre">HandField</span></code>）。所以 <code class="docutils literal notranslate"><span class="pre">__str()__</span></code> 方法会自动将 Python 对象转为字符串格式，帮你剩下不少时间。</li>
</ol>
</div>
</div>
<div class="section" id="s-writing-a-filefield-subclass">
<span id="writing-a-filefield-subclass"></span><h2>编写一个 <code class="docutils literal notranslate"><span class="pre">FileField</span></code> 子类<a class="headerlink" href="#writing-a-filefield-subclass" title="永久链接至标题">¶</a></h2>
<p>除了上述方法外，处理文件的字段还有一些必须考虑到的特殊要求。 <code class="docutils literal notranslate"><span class="pre">FileField</span></code> 提供的大部分机制（像是操作数据库存储和检索）能保持不变，让子类面对支持特殊文件的挑战。</p>
<p>Django 提供一个 <code class="docutils literal notranslate"><span class="pre">File</span></code> 类，作为文件内容和文件操作的代理。可以继承该类自定义访问文件的方式，哪些方法是可用的。它位于 <code class="docutils literal notranslate"><span class="pre">django.db.models.fields.files</span></code>，它的默认行为在 <a class="reference internal" href="../ref/files/file.html"><span class="doc">file 文档</span></a> 中介绍。</p>
<p>一旦创建了文件 <code class="docutils literal notranslate"><span class="pre">File</span></code> 子类，必须说明要使用新子类 <code class="docutils literal notranslate"><span class="pre">FileField</span></code>。为此，需要为 <code class="docutils literal notranslate"><span class="pre">FileField</span></code> 的子类的 <code class="docutils literal notranslate"><span class="pre">attr_class</span></code> 属性指定新的文件 <code class="docutils literal notranslate"><span class="pre">File</span></code> 子类。</p>
<div class="section" id="s-a-few-suggestions">
<span id="a-few-suggestions"></span><h3>一些建议<a class="headerlink" href="#a-few-suggestions" title="永久链接至标题">¶</a></h3>
<p>除了上述细节，下面还有一些准则，有助于极大地提高字段代码的效率和可读性。</p>
<ol class="arabic simple">
<li>Django 的 <code class="docutils literal notranslate"><span class="pre">ImageField</span></code> 的源码（位于 <code class="docutils literal notranslate"><span class="pre">django/db/models/fields/files.py</span></code>）就是个展示如何继承 <code class="docutils literal notranslate"><span class="pre">FileField</span></code> 支持特定文件的不错例子，因为它包含了上述所有技巧。</li>
<li>尽可能的缓存文件属性。因为文件可能保存在远端存储系统中，检出它们会消耗额外的时间，甚至是钱，且不总是必要的。一旦检出某个文件，获取其内容，尽可能缓存所有数据，以减少后续调用再次检索文件的次数。</li>
</ol>
</div>
</div>
</div>


          </div>
        </div>
      </div>
      
        
          <div class="yui-b" id="sidebar">
            
      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
        <div class="sphinxsidebarwrapper">
  <h3><a href="../contents.html">Table of Contents</a></h3>
  <ul>
<li><a class="reference internal" href="#">编写自定义模型字段(model fields)</a><ul>
<li><a class="reference internal" href="#introduction">介绍</a><ul>
<li><a class="reference internal" href="#our-example-object">我们的示例对象</a></li>
</ul>
</li>
<li><a class="reference internal" href="#background-theory">背后的理论</a><ul>
<li><a class="reference internal" href="#database-storage">数据库存储</a></li>
<li><a class="reference internal" href="#what-does-a-field-class-do">一个字段(Field)类做了什么？</a></li>
</ul>
</li>
<li><a class="reference internal" href="#writing-a-field-subclass">编写一个 field 子类</a><ul>
<li><a class="reference internal" href="#field-deconstruction">字段解析</a></li>
<li><a class="reference internal" href="#changing-a-custom-field-s-base-class">修改自定义字段的基类</a></li>
<li><a class="reference internal" href="#documenting-your-custom-field">为自定义字段编写文档</a></li>
<li><a class="reference internal" href="#useful-methods">实用方法</a><ul>
<li><a class="reference internal" href="#custom-database-types">自定义数据库类型</a></li>
<li><a class="reference internal" href="#converting-values-to-python-objects">将值转为 Python 对象</a></li>
<li><a class="reference internal" href="#converting-python-objects-to-query-values">将 Python 转为查询值</a></li>
<li><a class="reference internal" href="#converting-query-values-to-database-values">将查询值转为数据库值</a></li>
<li><a class="reference internal" href="#preprocessing-values-before-saving">在保存前预处理数值</a></li>
<li><a class="reference internal" href="#specifying-the-form-field-for-a-model-field">为模型字段指定表单字段</a></li>
<li><a class="reference internal" href="#emulating-built-in-field-types">仿造内置字段类型</a></li>
<li><a class="reference internal" href="#converting-field-data-for-serialization">为序列化转换字段数据</a></li>
</ul>
</li>
<li><a class="reference internal" href="#some-general-advice">一些通用建议</a></li>
</ul>
</li>
<li><a class="reference internal" href="#writing-a-filefield-subclass">编写一个 <code class="docutils literal notranslate"><span class="pre">FileField</span></code> 子类</a><ul>
<li><a class="reference internal" href="#a-few-suggestions">一些建议</a></li>
</ul>
</li>
</ul>
</li>
</ul>

  <h4>上一个主题</h4>
  <p class="topless"><a href="custom-management-commands.html"
                        title="上一章">编写自定义 <code class="docutils literal notranslate"><span class="pre">django-admin</span></code> 命令</a></p>
  <h4>下一个主题</h4>
  <p class="topless"><a href="custom-lookups.html"
                        title="下一章">自定义查询器</a></p>
  <div role="note" aria-label="source link">
    <h3>本页</h3>
    <ul class="this-page-menu">
      <li><a href="../_sources/howto/custom-model-fields.txt"
            rel="nofollow">显示源代码</a></li>
    </ul>
   </div>
<div id="searchbox" style="display: none" role="search">
  <h3>快速搜索</h3>
    <div class="searchformwrapper">
    <form class="search" action="../search.html" method="get">
      <input type="text" name="q" />
      <input type="submit" value="转向" />
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>
    </div>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
              <h3>Last update:</h3>
              <p class="topless">7月 23, 2021</p>
          </div>
        
      
    </div>

    <div id="ft">
      <div class="nav">
    &laquo; <a href="custom-management-commands.html" title="编写自定义 &lt;code class=&#34;docutils literal notranslate&#34;&gt;&lt;span class=&#34;pre&#34;&gt;django-admin&lt;/span&gt;&lt;/code&gt; 命令">previous</a>
     |
    <a href="index.html" title="操作指南" accesskey="U">up</a>
   |
    <a href="custom-lookups.html" title="自定义查询器">next</a> &raquo;</div>
    </div>
  </div>

      <div class="clearer"></div>
    </div>
  </body>
</html>